#include "cubemap_util.h"

/** From http://developer.nvidia.com/object/cube_map_ogl_tutorial.html:

    major axis
    direction     target                              sc     tc    ma
    ----------    ---------------------------------   ---    ---   ---
     +rx          GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT   -rz    -ry   rx
     -rx          GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT   +rz    -ry   rx
     +ry          GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT   +rx    +rz   ry
     -ry          GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT   +rx    -rz   ry
     +rz          GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT   +rx    -ry   rz
     -rz          GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT   -rx    -ry   rz

 */

void cubemapXYZFromST(GLfloat& x, GLfloat& y, GLfloat& z, GLenum face, GLfloat s, GLfloat t) {
    GLfloat sc = s * 2.0 - 1.0;
    GLfloat tc = t * 2.0 - 1.0;
    GLfloat ma = 1.0;

    switch(face) {
        case 0:
        case GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT:
            x = ma; z = -sc; y = -tc; return;

        case 1:
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT:
            x = -ma; z = sc; y = -tc; return;

        case 2:
        case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT:
            y = ma; x = sc; z = tc; return;

        case 3:
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT:
            y = -ma; x = sc; z = -tc; return;

        case 4:
        case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT:
            z = ma; x = sc; y = -tc; return;

        case 5:
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT:
            z = -ma; x = -sc; y = -tc; return;
    }
}

GLfloat lambertian(GLfloat ix, GLfloat iy, GLfloat iz, GLfloat vx, GLfloat vy, GLfloat vz) {
    GLfloat imag = normalize(ix, iy, iz);
    normalize(vx, vy, vz);
    //divide by the "distance" cubed: two for the distance dropoff in the solid angle of the pixel, and once more for the obliquity factor
    GLfloat c = dot(ix, iy, iz, vx, vy, vz) / (imag * imag * imag);
    return max(c, 0.0f);
}

GLfloat y00(GLfloat x, GLfloat y, GLfloat z) {
    GLfloat mag = normalize(x, y, z);
    return 0.282095 / (mag * mag * mag);
}

GLfloat y11(GLfloat x, GLfloat y, GLfloat z) {
    GLfloat mag = normalize(x, y, z);
    return 0.488603 * x / (mag * mag * mag);
}

GLfloat y10(GLfloat x, GLfloat y, GLfloat z){
    GLfloat mag = normalize(x, y, z);
    return 0.488603 * z / (mag * mag * mag);
}

GLfloat y1n1(GLfloat x, GLfloat y, GLfloat z){
    GLfloat mag = normalize(x, y, z);
    return 0.488603 * y / (mag * mag * mag);
}

GLfloat y22(GLfloat x, GLfloat y, GLfloat z){
    GLfloat mag = normalize(x, y, z);
    return 0.546274 * (x*x - y*y) / (mag * mag * mag);
}

GLfloat y21(GLfloat x, GLfloat y, GLfloat z){
    GLfloat mag = normalize(x, y, z);
    return 1.092548 * x * z / (mag * mag * mag);
}

GLfloat y20(GLfloat x, GLfloat y, GLfloat z){
    GLfloat mag = normalize(x, y, z);
    return 0.315392 * (3*z*z - 1) / (mag * mag * mag);
}

GLfloat y2n1(GLfloat x, GLfloat y, GLfloat z){
    GLfloat mag = normalize(x, y, z);
    return 1.092548 * y * z / (mag * mag * mag);
}

GLfloat y2n2(GLfloat x, GLfloat y, GLfloat z){
    GLfloat mag = normalize(x, y, z);
    return 1.092548 * x * y / (mag * mag * mag);
}

/**
 * weightings on the normal vector side
 */

GLfloat y00e(GLfloat x, GLfloat y, GLfloat z) {
    return 0.886227;
}

GLfloat y11e(GLfloat x, GLfloat y, GLfloat z) {
    normalize(x, y, z);
    return 2 * 0.511664 * x;
}

GLfloat y10e(GLfloat x, GLfloat y, GLfloat z){
    normalize(x, y, z);
    return 2 * 0.511664 * z;
}

GLfloat y1n1e(GLfloat x, GLfloat y, GLfloat z){
    normalize(x, y, z);
    return 2 * 0.511664 * y;
}

GLfloat y22e(GLfloat x, GLfloat y, GLfloat z){
    normalize(x, y, z);
    return 0.429043 * (x*x - y*y);
}

GLfloat y21e(GLfloat x, GLfloat y, GLfloat z){
    normalize(x, y, z);
    return 2 * 0.429043 * x * z;
}

GLfloat y20e(GLfloat x, GLfloat y, GLfloat z){
    normalize(x, y, z);
    return 0.247708 * (3*z*z - 1);
}

GLfloat y2n1e(GLfloat x, GLfloat y, GLfloat z){
    normalize(x, y, z);
    return 2 * 0.429043 * y * z;
}

GLfloat y2n2e(GLfloat x, GLfloat y, GLfloat z){
    normalize(x, y, z);
    return 2 * 0.429043 * x * y;
}
